#include "stdafx.h"
#include "TF3DHooker.h"

TF3DHooker* TF3DHooker::_instance = 0;


TF3DHooker::TF3DHooker()
{
	_instance = this;
	navigate = true;
}

HWND TF3DHooker::GetManilaWindow()
{
	return FindWindow (L"Manila", 0);
}

TF3DHooker* TF3DHooker::get_Instance()
{
	if (!_instance)
		_instance = new TF3DHooker();
	return _instance;
}

void TF3DHooker::Initialize(Logger* logger, TODAYTABSETTINGS* settings)
{
	_logger = logger;
	_settings = settings;
}

void TF3DHooker::StaticNavigateInCompleteInfo(LPVOID a1, WCHAR* a2, LPVOID a3)
{
	get_Instance()->NavigateInCompleteInfo(a1, a2, a3);
}

void TF3DHooker::NavigateInCompleteInfo(LPVOID a1, WCHAR* a2, LPVOID a3)
{
	NKDbgPrintfW(L"---NavigateInCompleteInfo---");
	_logger->Log(L"NavigateInComplete");
	
	void (*aNavigateInComp)(LPVOID a1, LPVOID a2);
	aNavigateInComp = (void (*)(LPVOID, LPVOID))appliedPatch.NavigateInComplete.OrgnFn;
	aNavigateInComp(a1, a2);

	navigate = true;

	NKDbgPrintfW(L"---NavigateInCompleteInfo END---");
}

DWORD TF3DHooker::StaticPrintPageName(LPVOID a1, WCHAR* a2, LPVOID a3, LPVOID a4)
{
	return get_Instance()->PrintPageName(a1, a2, a3, a4);
}

DWORD TF3DHooker::PrintPageName(LPVOID a1, WCHAR* a2, LPVOID a3, LPVOID a4)
{
	NKDbgPrintfW(L"---PrintPageName---");
	_logger->Log(L"PrintPageName: %s", a2);
	
	DWORD (*PrintPageNameOrg)(LPVOID a1, LPVOID a2, LPVOID a3, LPVOID a4);
	PrintPageNameOrg = (DWORD (*)(LPVOID, LPVOID, LPVOID, LPVOID))appliedPatch.PrintPageName.OrgnFn;
	DWORD value = PrintPageNameOrg(a1, a2, a3, a4);

	if (wcsstr(a2, L".dialog"))
		return value;

	if (navigate)
	{
		_callback(wcsicmp(_settings->szActivePage, a2) == 0);
		navigate = false;
	}

	NKDbgPrintfW(L"---PrintPageName END---");
	return value;
}

bool TF3DHooker::ApplyPatch(MANILA_TODAY_PATCH_SETTINGS* patch)
{
	HANDLE currentProcess = GetCurrentProcess();

	DWORD data;
	ReadProcessMemory(currentProcess, (LPVOID)patch->NavigateInComplete.FnAddr, &data, sizeof(DWORD), 0);
	if (data != patch->NavigateInComplete.OrgnFnCall)
		return 0;

	ReadProcessMemory(currentProcess, (LPVOID)patch->PrintPageName.FnAddr, &data, sizeof(DWORD), 0);
	if (data != patch->PrintPageName.OrgnFnCall)
		return 0;
	
	appliedPatch = *patch;
	
	ReplaceProc(appliedPatch.PrintPageName.FnAddr, (DWORD)StaticPrintPageName);
	ReplaceProc(appliedPatch.NavigateInComplete.FnAddr, (DWORD)StaticNavigateInCompleteInfo);

	return 1;
}

void TF3DHooker::Hook(PageChangeCallbackFn pageChangeCallback)
{
	_logger->Log(L"TF3DHooker -> Hook");	
	_callback = pageChangeCallback;
	
	/* GODMODE ON */
	BOOL  bOldKMode = SetKMode ( TRUE );
	DWORD dOldPerms = SetProcPermissions ( 0xFFFFFFFF );

	bool patchApplied = false;

	memset(&appliedPatch, 0, sizeof(MANILA_TODAY_PATCH_SETTINGS));
	MANILA_TODAY_PATCH_SETTINGS lastPatch;
	_logger->Log(L"Loading last patch..");	
	if (GetLastAppliedPatch(&lastPatch))
	{
		_logger->Log(L"Patch loaded.");	
		if (ApplyPatch(&lastPatch))
		{
			_logger->Log(L"Last patch applied.");	
			patchApplied = true;
		}
	}
	
	if (!patchApplied)
	{
		list<MANILA_TODAY_PATCH_SETTINGS> patchData;
		_logger->Log(L"Loading existing patches...");	
		LoadPatches(&patchData);
		
		list<MANILA_TODAY_PATCH_SETTINGS>::iterator i;
		for (i = patchData.begin(); i != patchData.end(); i++)
		{
			if (ApplyPatch(&*i))
			{
				_logger->Log(L"Found patch to apply. Applied. Saving last patch.");	
				patchApplied = true;
				SaveLastAppliedPatch(&*i);
			}
		}

		if (!patchApplied)
		{
			_logger->Log(L"Existing patch not found. Trying to calculate...");	
			int result = GenCode(L"\\Windows\\manil2.exe", &lastPatch);
			if (result == ERROR_FILE_NOT_FOUND)
			{
				result = GenCode(L"\\Windows\\manila2.exe", &lastPatch);
				if (result == ERROR_FILE_NOT_FOUND)
				{
					result = GenCode(L"\\Windows\\manila.exe", &lastPatch);
				}
			}

			if (result == ERROR_FILE_NOT_FOUND)
			{
				_logger->Log(L"Unable to find manila.exe nor manil2.exe nor manila2.exe.");	
				MessageBox(0, L"Unable to find manila.exe nor manil2.exe nor manila2.exe.", L"MTPLoader", 0);
			}

			if (result == 1)
			{
				_logger->Log(L"Patch successfuly calculated");	
				if (ApplyPatch(&lastPatch))
				{
					_logger->Log(L"Patch applied. Saving last patch.");	
					patchApplied = true;
					SaveLastAppliedPatch(&lastPatch);
				}
				else
				{
					_logger->Log(L"Unable to apply calculated patch.");	
					MessageBox(0, L"Unable to apply calculated patch.", L"MTPLoader", 0);
				}
			}

			if (!patchApplied && result != 1)
			{
				_logger->Log(L"Unable to calculate patch. This version of manila.exe is not supported.");
				MessageBox(0, L"Unable to calculate patch. This version of manila.exe is not supported.", L"MTPLoader", 0);
			}
		}
	}

	/* GODMODE OFF */
	SetProcPermissions ( dOldPerms );
	SetKMode ( bOldKMode );

	_logger->Log(L"TF3DHooker -> Hook Done!");
}


void TF3DHooker::UnHook()
{
	PROCESS * prc = NULL;
	/* GODMODE ON */
	BOOL  bOldKMode = SetKMode ( TRUE );
	DWORD dOldPerms = SetProcPermissions ( 0xFFFFFFFF );

	if (appliedPatch.NavigateInComplete.FnAddr = 0) 
		return; // no patch applied
	
	WriteProcessMemory(GetCurrentProcess(), (LPVOID)appliedPatch.PrintPageName.FnAddr, &appliedPatch.PrintPageName.OrgnFnCall, sizeof(DWORD), 0);
	WriteProcessMemory(GetCurrentProcess(), (LPVOID)appliedPatch.NavigateInComplete.FnAddr, &appliedPatch.NavigateInComplete.FnAddr, sizeof(DWORD), 0);

	/* GODMODE OFF */
	SetProcPermissions ( dOldPerms );
	SetKMode ( bOldKMode );
}